<?php

class GUI {
	const DEBUG = TRUE;

	protected static $activeUser = NULL;

	protected static $bannerText = "";

	// -=-=-=-=-=-
	// entry point
	// -=-=-=-=-=-
	public static function run() {
		// register output handler
		ob_start(array("GUI", "ob_handler"));
		session_start();

		require_once("header.inc.php");
		
		try {
			self::verifyAuthentication();
			self::handleRequest();
		} catch (Exception $e) {
			// - clean output buffer
			ob_end_clean();
			ob_start(array("GUI", "ob_handler"));
			include("header.inc.php");

			// - display error message
			self::$bannerText = "Probleem";
			printf("<p>%s</p>\n", $e->getMessage());

			// todo log
			//
			if (self::DEBUG) {
				throw $e;
			}
		}
	}

	// -=-=-=-=-=-=-=-=-=-=-=-=
	// output buffering handler
	// -=-=-=-=-=-=-=-=-=-=-=-=
	// attaches footer to final output block
	// also sets banner text in the header
	public static function ob_handler($s, $flags) {
		if ($flags & PHP_OUTPUT_HANDLER_END) {
			$s .= file_get_contents("footer.inc.php");
		}
		$s = str_replace('$BANNER', self::$bannerText, $s);
		return $s;
	}

	// -=-=-=-=-=-=-=-=-=-=-=-=-=-=
	// verify auth status of client
	// -=-=-=-=-=-=-=-=-=-=-=-=-=-=
	// if the client is unknown, redirect it to a sign up page
	// store request for later to handle after signup is complete
	protected static function verifyAuthentication() {
		if (self::DEBUG) {
			if (!empty($_GET["forget"]) && $_GET["forget"]) {
				try {
					$user = BLL::getInstance()->getActiveUser();
					BLL::getInstance()->remove($user);
					unset($_SESSION["userID"]);
				} catch (Exception $e) {
					// probably no user active, ignore
					// todo differentiate between "no user active" and other exceptions
				}
			}
		}

		// check for re-authentication
		if (!empty($_GET["uid"])) {
			if (!BLL::getInstance()->verifyUserIDHash($_GET["uid"], $_GET["token"])) {
				self::showAuthenticationFailure();
				exit();
			}
			// token verified, set user id again
			$_SESSION["userID"] = $_GET["uid"];
		}

		// check for sign up in progress
		if (!empty($_GET["u"])) {
			self::handleSignUp();
		}
		// sign up is transparent, fall through to next request handling step
		
		if (empty($_SESSION["userID"])) {
			// display sign up form
			self::showSignup();
			// exit so the rest does not run
			exit();
		}
		// optional TODO: prevent cheating by verifying IP?
		
		self::$activeUser = BLL::getInstance()->getActiveUser();	// let exception bubble up if it is thrown
	}

	// -=-=-=-=-=-=-=-=-=-=-=-=-
	// START OF REQUEST HANDLING
	// -=-=-=-=-=-=-=-=-=-=-=-=-
	
	// Request parameters (in order of precedence)
	// 'uid': indicates a reauthentication for user with userID 'uid'
	// 'token': a crypto hash of 'uid'
	// 'u': indicates user sign up, optionally, 'a' and 'q' are present for subsequent handling
	// 'a': indicates answer given, question ID is in 'q'
	// 'q': if alone, indicates question to display
	// 'h': a crypto hash of 'q'. always accompanies 'q' to prevent question guessing
	// empty: no parameters indicates a request to see the dashboard
	
	// -=-=-=-=-=-=-=-=-=-=-=-
	// main request dispatcher
	// -=-=-=-=-=-=-=-=-=-=-=-
	protected static function handleRequest() {
		// verify question signature
		if (!empty($_GET["q"])) {
			self::verifyQuestionSignature();
		}

		// dispatch
		if (!empty($_GET["a"])) {
			self::handleAnswer();
		} else if (!empty($_GET["q"])) {
			self::handleQuestion();
		} else {
			self::handleDashboard();
		}
	}

	// -=-=-=-=-=-=-=-=-=-=-
	// handle sign up action
	// -=-=-=-=-=-=-=-=-=-=-
	protected static function handleSignUp() {
		$bll = BLL::getInstance();

		// check for empty e-mail address
		if (empty($_GET["u"])) {
			throw new Exception("Geen naam opgegeven");
		}
		if (empty($_GET["e"])) {
			throw new Exception("Geen e-mailadres opgegeven");
		}

		// look up user by supplied e-mail address (careful about case sensitivity)
		$user = $bll->getUserByEmail($_GET["e"]);
		// if user exists
		//  -> refer to re-auth link in e-mail
		//  -> offer to re-send re-auth link
		if (!empty($user)) {
			self::showReauthenticateInstructions();
			exit();
		}

		// if user does not exists
		//  -> create new user
		//  -> store userID in cookie
		//  -> send re-auth link to e-mail
		$user = new User();
		$user->email = $_GET["e"];
		$user->name = $_GET["u"];
		$bll->saveUser($user);
		$_SESSION["userID"] = $user->id;
		$bll->sendWelcome($user);
	}

	// -=-=-=-=-=-=-=-=-=-=
	// handle answer action
	// -=-=-=-=-=-=-=-=-=-=
	protected static function handleAnswer() {
		$bll = BLL::getInstance();
		// check for valid 'q'
		$question = self::getQuestionFromRequest();

		// check user has not answered question yet
		if ($bll->currentUserHasAnswered($question)) {
			self::showQuestionAlreadyAnsweredPage();
			return;
		}

		// look up answer 'a' in db, check it exists and belongs to q
		if (empty($_GET["a"])) {
			throw new Exception("Geen antwoord opgegeven");
		}
		$answer = $bll->getPossibleAnswerByID($_GET["a"]);
		if (empty($answer)) {
			throw new Exception("Onbekend antwoord");
		}
		if ($answer->question->id != $question->id) {
			throw new Exception("Dit antwoord hoort niet bij deze vraag");
		}
		// record answer
		$bll->giveAnswer($answer);
		self::showQuestionAnsweredPage();
	}

	// -=-=-=-=-=-=-=-=-=-=-=
	// handle question action
	// -=-=-=-=-=-=-=-=-=-=-=
	protected static function handleQuestion() {
		$bll = BLL::getInstance();
		// check for valid 'q'
		$question = self::getQuestionFromRequest();
		// check user has not answered question yet
		if ($bll->currentUserHasAnswered($question)) {
			self::showQuestionAlreadyAnsweredPage();
			return;
		}

		// render question page
		self::showQuestionPage($question);
	}

	// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
	// handle dashboard (scoreboard) action
	// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
	protected static function handleDashboard() {
		self::$bannerText = "QrUIZ";
		$user = BLL::getInstance()->getActiveUser();
		if (!empty($user->achievements)) {
			printf("<p>Je hebt de volgende achievements al behaald:</p>\n");
			printf("<ul class=\"achievementObtained\">\n");
			foreach ($user->achievements as $achievement) {
				printf("<li>%s <small>(%s)</small></li>\n", $achievement->description, $achievement->explanation);
			}
			printf("</ul>\n");
		}
		printf("<p>Ga op zoek naar QR-tags in het atrium om vragen te beantwoorden!</p>\n");
	}

	// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
	// get a question from request data (with error checking)
	// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
	protected static function getQuestionFromRequest() {
		$bll = BLL::getInstance();
		if (empty($_GET["q"])) {
			throw new Exception("Geen vraag opgegeven");
		}
		$question = $bll->getQuestionByID($_GET["q"]);
		if (empty($question)) {
			throw new Exception("Deze vraag bestaat niet");
		}
		return $question;
	}

	// -=-=-=-=-=-=-=-=-=-=
	// verify a q signature
	// -=-=-=-=-=-=-=-=-=-=
	protected static function verifyQuestionSignature() {
		if (empty($_GET["h"])) {
			throw new Exception("Vraag kan niet gevalideerd worden");
		}
		if (!BLL::getInstance()->verifyQuestionHash($_GET["q"], $_GET["h"])) {
			self::$bannerText = "Niet toegestaan";
			printf("<p>Deze vraag is niet correct ondertekend.</p>\n");
		}
	}

	// -=-=-=-=-=-=-=-=-=-=-=-=-
	// show a question to answer
	// -=-=-=-=-=-=-=-=-=-=-=-=-
	protected static function showQuestionPage(Question $question) {
		if (empty($question->answers)) {
			throw new Exception("Helaas is er een probleem met deze vraag");
		}

		self::$bannerText = sprintf("Vraag %s", $question->id);
		$s = "";
		$s .= sprintf("<p>Hallo %s,</p>\n", BLL::getInstance()->getActiveUser()->name);
		$s .= sprintf("<form id=\"question\" action=\"%s\">\n", $_SERVER["PHP_SELF"]);
		$s .= sprintf("<input type=\"hidden\" name=\"q\" value=\"%s\">\n", $question->id);
		$s .= sprintf("<input type=\"hidden\" name=\"h\" value=\"%s\">\n", BLL::generateQuestionHash($question->id));

		$s .= "<p class=\"label-question\">\n";
		$s .= sprintf("<label for=\"a\">%s</label>\n", $question->questionText);
		$s .= "<ul>\n";
		foreach ($question->answers as $answer) {
			$s .= sprintf("<li> <INPUT type=\"radio\" name=\"a\" value=\"%s\" class=\"styled\"> %s</li>\n", $answer->id, $answer->answerText);
		}
		$s .= "</ul>\n";
		$s .= "</p>\n";

		$s .= "<p class=\"submit-wrapper\">\n";
		$s .= "<input class=\"button-primary\" type=\"submit\" value=\"Verstuur antwoord\">\n";
		$s .= "</p>\n";
		
		$s .= "<br class=\"clear\">\n";
		$s .= "</form>\n";

		print($s);
	}

	// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
	// thank a user for answering a question
	// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
	protected static function showQuestionAnsweredPage() {
		self::$bannerText = "Antwoord verstuurd";
		printf("<p>Bedankt, je antwoord is verzonden.</p>\n");
		$achievementsObtained = BLL::getInstance()->getObtainedAchievements();
		if (!empty($achievementsObtained)) {
			if (count($achievementsObtained) == 1) {
				printf("<p>Je hebt zojuist een achievement behaald!</p>\n");
			} else {
				printf("<p>Je hebt zojuist de volgende achievements behaald!</p>\n");
			}
			printf("<ul class=\"achievementObtained\">\n");
			foreach ($achievementsObtained as $achievement) {
				printf("<li>%s</li>\n", $achievement->description);
			}
			printf("</ul>\n");
		}
		printf("<p>Ga op zoek naar de andere QR tags om nog meer vragen te beantwoorden.</p>\n");
	}

	// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
	// tell a user he already answered this question
	// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
	protected static function showQuestionAlreadyAnsweredPage() {
		self::$bannerText = "Helaas";
		printf("<p>Deze vraag heb je al beantwoord!</p><p>Ga op zoek naar de andere QR tags om nog meer vragen te beantwoorden.</p>\n");
	}

	// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
	// tell a user to look in his/her e-mail
	// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
	protected static function showReauthenticateInstructions() {
		self::$bannerText = "Al aangemeld";
		printf("<p>Bij het aanmelden heb je een e-mail ontvangen waarmee je je telefoon opnieuw kunt aanmelden. Open deze link op je telefoon om opnieuw aan te melden.</p>\n");
	}

	// -=-=-=-=-=-=-=-=-=-=-=-
	// allow a user to sign up
	// -=-=-=-=-=-=-=-=-=-=-=-
	protected static function showSignup() {
		self::$bannerText = "Welkom bij de QrUIZ";
		printf("<p>Je bent nog niet bekend in het systeem. <br><br>Om de antwoorden op te kunnen slaan, moet je eenmalig je naam en e-mailadres opgeven.</p>\n");
		printf("<form id=\"AddPersonForm\" action=\"%s\">\n", $_SERVER["PHP_SELF"]);
		// duplicate GET parameters so users can continue transparently
		foreach ($_GET as $key => $value) {
			if (self::DEBUG && $key == "forget") continue;
			printf("<input type=\"hidden\" name=\"%s\" value=\"%s\">\n", $key, $value);
		}
		printf("<p class=\"label-field-wrapper\">\n");
		printf("Naam: <input id=\"PersonName\" type=\"text\" name=\"u\"><br>\n");
		printf("E-mail: <input id=\"PersonEmail\" type=\"text\" name=\"e\">\n");
		printf("</p>\n");
		printf("<p class=\"submit-wrapper\">\n");
		printf("<input class=\"button-primary\" type=\"submit\" value=\"Ik doe mee!\">\n");
		printf("</p>\n");
		printf("<br class=\"clear\">\n");
		printf("</form>\n");
	}
}
